package club.kynb.mall.application.assembler;


import club.kynb.mall.application.model.model.dto.*;
import club.kynb.mall.application.model.model.dto.UserShoppingCartItemDTO.CartSkuDTO;
import club.kynb.mall.application.model.model.dto.UserShoppingCartItemDTO.CartSkuPriceDTO;
import club.kynb.mall.application.model.model.dto.UserShoppingCartItemDTO.CartSpuDTO;
import club.kynb.mall.order.model.dto.ShoppingCartDTO;
import club.kynb.mall.product.constant.UserCouponStatusEnum;
import club.kynb.mall.product.dto.CouponTemplateDTO;
import club.kynb.mall.product.dto.ProductSkuDTO;
import club.kynb.mall.product.dto.ProductSpuDetailDTO;
import club.kynb.mall.product.dto.UserCouponDTO;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.constant.GlobalConstant;
import org.pizza.util.Checker;
import org.pizza.util.Convertor;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static club.kynb.mall.order.constant.ShoppingCartConstant.ShoppingCartStatusEnum;

/**
 * @author kynb_club@163.com
 * @since 2021/6/24 9:02 上午
 * 装配器，用于聚合转换各个领域DTO对象
 */
@Slf4j
public class MallCommonAssembler {

    public static List<UserReceiveCouponDTO> assembleUserReceiveCouponDTO(List<UserCouponDTO> userCouponList, List<CouponTemplateDTO> templateList) {
        final Map<Long, CouponTemplateDTO> templateMap = templateList.stream().collect(Collectors.toMap(CouponTemplateDTO::getId, templateDTO -> templateDTO, (t1, t2) -> t2));
        return userCouponList.stream()
                .filter(userCouponDTO -> {
                    final boolean containsKey = templateMap.containsKey(userCouponDTO.getCouponTemplateId());
                    if (!containsKey) {
                        log.error("数据异常，用户优惠券ID：{} 优惠券模板ID：{} 没有找到模板数据，请排查", userCouponDTO.getId(), userCouponDTO.getCouponTemplateId());
                    }
                    return containsKey;
                })
                .map(userCouponDTO -> convertUserReceiveCouponDTO(userCouponDTO, templateMap))
                .collect(Collectors.toList());
    }

    private static UserReceiveCouponDTO convertUserReceiveCouponDTO(UserCouponDTO userCouponDTO, Map<Long, CouponTemplateDTO> templateMap) {
        final CouponTemplateDTO couponTemplateDTO = templateMap.get(userCouponDTO.getCouponTemplateId());
        final UserReceiveCouponDTO userReceiveCouponDTO = new UserReceiveCouponDTO();
        userReceiveCouponDTO.setId(userCouponDTO.getId());
        userReceiveCouponDTO.setUserId(userCouponDTO.getUserId());
        userReceiveCouponDTO.setReceiveTime(userCouponDTO.getReceiveTime());
        final long betweenDay = DateUtil.between(couponTemplateDTO.getInvalidTime(), new Date(), DateUnit.DAY);
        final long betweenHour = DateUtil.between(couponTemplateDTO.getInvalidTime(), new Date(), DateUnit.HOUR);
        final long betweenMinute = DateUtil.between(couponTemplateDTO.getInvalidTime(), new Date(), DateUnit.MINUTE);
        if (log.isDebugEnabled()) {
            log.debug("优惠券距离过期时间：{} 天", betweenDay);
            log.debug("优惠券距离过期时间：{} 小时", betweenHour);
            log.debug("优惠券距离过期时间：{} 分钟", betweenMinute);
        }
        if (betweenDay <= 0) {
            if (betweenHour <= 0) {
                if (betweenMinute <= 0) {
                    userReceiveCouponDTO.setStatus(UserCouponStatusEnum.OVERDUE.getStatus());
                    userReceiveCouponDTO.setStatusInfo(UserCouponStatusEnum.OVERDUE.getDesc());
                } else {
                    userReceiveCouponDTO.setStatus(UserCouponStatusEnum.UN_USE.getStatus());
                    userReceiveCouponDTO.setStatusInfo(String.format(UserCouponStatusEnum.NEAR_OVERDUE_MINUTE.getDesc(), betweenMinute));
                }
            } else {
                userReceiveCouponDTO.setStatus(UserCouponStatusEnum.UN_USE.getStatus());
                userReceiveCouponDTO.setStatusInfo(String.format(UserCouponStatusEnum.NEAR_OVERDUE_HOUR.getDesc(), betweenHour));
            }
        } else {
            userReceiveCouponDTO.setStatus(UserCouponStatusEnum.UN_USE.getStatus());
            userReceiveCouponDTO.setStatusInfo(String.format(UserCouponStatusEnum.NEAR_OVERDUE_DAY.getDesc(), betweenDay));
        }
        userReceiveCouponDTO.setName(couponTemplateDTO.getName());
        userReceiveCouponDTO.setTitle(couponTemplateDTO.getTitle());
        userReceiveCouponDTO.setDescription(couponTemplateDTO.getDescription());
        userReceiveCouponDTO.setTag(couponTemplateDTO.getTag());
        userReceiveCouponDTO.setCouponValue(String.valueOf(couponTemplateDTO.getCouponValue()));
        userReceiveCouponDTO.setCouponType(couponTemplateDTO.getCouponType());
        userReceiveCouponDTO.setUseSuperimposedType(couponTemplateDTO.getUseSuperimposedType());
        userReceiveCouponDTO.setReceiveWay(couponTemplateDTO.getReceiveWay());
        userReceiveCouponDTO.setEffectiveTime(couponTemplateDTO.getEffectiveTime());
        userReceiveCouponDTO.setInvalidTime(couponTemplateDTO.getInvalidTime());
        userReceiveCouponDTO.setSpuRange(couponTemplateDTO.getSpuRange());
        userReceiveCouponDTO.setSpuIds(couponTemplateDTO.getSpuIds());
        userReceiveCouponDTO.setReturnQualification(couponTemplateDTO.getReturnQualification());
        userReceiveCouponDTO.setThresholdAmount(String.valueOf(couponTemplateDTO.getThresholdAmount()));
        return userReceiveCouponDTO;
    }

    public static List<UserCouponTemplateDTO> assembleUserCouponTemplateDTO(List<UserCouponDTO> userCouponList, List<CouponTemplateDTO> templateList) {
        final Map<Long, UserCouponDTO> userCouponMap = userCouponList.stream().collect(Collectors.toMap(UserCouponDTO::getCouponTemplateId, userCouponDTO -> userCouponDTO, (t1, t2) -> t2));
        final List<UserCouponTemplateDTO> userCouponTemplateList = Convertor.convert(templateList, UserCouponTemplateDTO.class);
        return userCouponTemplateList
                .stream()
                .peek(userCouponTemplateDTO -> {
                    userCouponTemplateDTO.setIsDraw(userCouponMap.containsKey(userCouponTemplateDTO.getId()) ? GlobalConstant.YES : GlobalConstant.NO);
                    userCouponTemplateDTO.setResidueRatio(NumberUtil.div(String.valueOf(userCouponTemplateDTO.getResidueQuantity()), String.valueOf(userCouponTemplateDTO.getReleaseQuantity())));
                }).collect(Collectors.toList());
    }

    public static UserShoppingCartDTO assembleUserShoppingCartDTO(List<ShoppingCartDTO> dtoList, List<ProductSpuDetailDTO> spuList) {
        final Map<String, ProductSpuDetailDTO> spuMap = spuList.stream().collect(Collectors.toMap(ProductSpuDetailDTO::getId, spuDTO -> spuDTO, (t1, t2) -> t2));
        final UserShoppingCartDTO cartDTO = new UserShoppingCartDTO();
        //转换
        final List<UserShoppingCartItemDTO> cartItemList = dtoList
                .stream()
                .map(shoppingCartDTO -> convertUserShoppingCartDTO(shoppingCartDTO, spuMap))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        //价格汇总
        final Optional<BigDecimal> bigDecimal = cartItemList.stream()
                .filter(userShoppingCartItemDTO -> userShoppingCartItemDTO.getChecked().equals(GlobalConstant.YES))
                .map(userShoppingCartItemDTO -> {
                    final BigDecimal skuPrice = userShoppingCartItemDTO.getCartSpuDTO().getCartSkuDTO().getSkuPriceDTO().getSkuPrice();
                    final BigDecimal skuNum = new BigDecimal(String.valueOf(userShoppingCartItemDTO.getCartSpuDTO().getCartSkuDTO().getSkuNum()));
                    return NumberUtil.mul(skuNum, skuPrice);
                })
                .reduce(BigDecimal::add);
        cartDTO.setTotalAmount(bigDecimal.orElse(BigDecimal.ZERO));
        cartDTO.setItemList(cartItemList);
        return cartDTO;
    }

    private static UserShoppingCartItemDTO convertUserShoppingCartDTO(ShoppingCartDTO shoppingCartDTO, Map<String, ProductSpuDetailDTO> spuMap) {
        final ProductSpuDetailDTO spuDTO = spuMap.get(shoppingCartDTO.getSpuId());
        if (spuDTO == null) {
            log.error("购物id:{} 对应 spuId：{} ,没有找到SPU实体对象", shoppingCartDTO.getId(), shoppingCartDTO.getSpuId());
            return null;
        }
        final List<ProductSkuDTO> skus = spuDTO.getSkuList();
        final Map<String, ProductSkuDTO> skuMap = skus.stream().collect(Collectors.toMap(ProductSkuDTO::getId, sku -> sku, (t1, t2) -> t2));
        final ProductSkuDTO skuDTO = skuMap.get(shoppingCartDTO.getSkuId());
        if (skuDTO == null) {
            log.error("购物id:{} 对应 spuId：{} skuId:{} ,没有找到SKU实体对象", shoppingCartDTO.getId(), shoppingCartDTO.getSpuId(), shoppingCartDTO.getSkuId());
            return null;
        }
        final UserShoppingCartItemDTO userShoppingCartItemDTO = new UserShoppingCartItemDTO();
        userShoppingCartItemDTO.setId(shoppingCartDTO.getId());
        final UserShoppingCartItemDTO.CartSpuDTO cartSpuDTO = new CartSpuDTO();
        cartSpuDTO.setId(shoppingCartDTO.getSpuId());
        cartSpuDTO.setSpuName(spuDTO.getSpuName());
        cartSpuDTO.setIconUrl(spuDTO.getSpuImgs());
        final CartSkuDTO cartSkuDTO = new CartSkuDTO();
        cartSkuDTO.setId(shoppingCartDTO.getSkuId());
        cartSkuDTO.setSpuId(spuDTO.getId());
        cartSkuDTO.setSkuName(skuDTO.getSkuName());
        cartSkuDTO.setSkuUnit(skuDTO.getSkuUnit());
        cartSkuDTO.setSkuNum(shoppingCartDTO.getSkuNum());
        final CartSkuPriceDTO cartSkuPriceDTO = new CartSkuPriceDTO();
        cartSkuPriceDTO.setSkuPrice(skuDTO.getSkuPrice());
        cartSkuPriceDTO.setUnit(skuDTO.getSkuUnit());
        cartSkuDTO.setSkuPriceDTO(cartSkuPriceDTO);
        cartSpuDTO.setCartSkuDTO(cartSkuDTO);
        userShoppingCartItemDTO.setCartSpuDTO(cartSpuDTO);
        userShoppingCartItemDTO.setChecked(shoppingCartDTO.getChecked());
        //TODO 库存
        userShoppingCartItemDTO.setCurrentStatus(ShoppingCartStatusEnum.NORMAL.getStatus());
        userShoppingCartItemDTO.setTips("");
        userShoppingCartItemDTO.setExtension("");
        return userShoppingCartItemDTO;
    }

    public static List<PreviewOrderSpuDTO> assembleOrderSpuList(List<ShoppingCartDTO> checkedList, List<ProductSpuDetailDTO> filteredList) {
        final Map<String, ProductSpuDetailDTO> dtoMap = filteredList.stream().collect(Collectors.toMap(spu->String.valueOf(spu.getId()), productSpuDTO -> productSpuDTO, (p1, p2) -> p2));
        return checkedList.stream().map(cartDTO -> {
            final ProductSpuDetailDTO productSpuDTO = dtoMap.get(cartDTO.getSpuId());
            PreviewOrderSpuDTO previewOrderSpuDTO = new PreviewOrderSpuDTO();
            previewOrderSpuDTO.setId(productSpuDTO.getId());
            previewOrderSpuDTO.setSpuName(productSpuDTO.getSpuName());
            previewOrderSpuDTO.setIconUrl(productSpuDTO.getSpuImgs());
            final List<ProductSkuDTO> skus = productSpuDTO.getSkuList();
            Checker.ifEmptyThrow(skus, () -> {
                log.error("转换订单Spu对象失败，spuId：{}，没有找到sku", productSpuDTO.getId());
                return Errors.SYSTEM.exception("装配订单信息系统异常，请通知开发人员排查");
            });
            //寻找购物车对应的sku信息
            final Optional<ProductSkuDTO> skuDTOOptional = skus.stream().filter(skuDTO -> skuDTO.getId().equals(cartDTO.getSkuId())).findFirst();
            final ProductSkuDTO productSkuDTO = skuDTOOptional.orElseThrow(() -> Errors.SYSTEM.exception("装配订单信息系统异常，没找到匹配的sku信息，请通知开发人员排查"));
            final PreviewOrderSkuDTO previewOrderSkuDTO = new PreviewOrderSkuDTO();
            previewOrderSkuDTO.setId(productSkuDTO.getId().toString());
            previewOrderSkuDTO.setSpuId(productSpuDTO.getId());
            previewOrderSkuDTO.setSkuName(productSkuDTO.getSkuName());
            previewOrderSkuDTO.setSkuUnit(productSkuDTO.getSkuUnit());
            previewOrderSkuDTO.setSkuNum(cartDTO.getSkuNum());

            final PreviewOrderSkuPriceDTO previewOrderSkuPriceDTO = new PreviewOrderSkuPriceDTO();
            previewOrderSkuPriceDTO.setSkuPrice(productSkuDTO.getSkuPrice());
            previewOrderSkuPriceDTO.setUnit(productSkuDTO.getSkuUnit());
            previewOrderSkuDTO.setSkuPriceDTO(previewOrderSkuPriceDTO);
            previewOrderSpuDTO.setPreviewOrderSkuDTO(previewOrderSkuDTO);
            return previewOrderSpuDTO;
        }).collect(Collectors.toList());
    }
}
